import {
  BadRequestException,
  createParamDecorator,
  ExecutionContext,
} from '@nestjs/common';
import { Request } from 'express';
import {
  IsNull,
  Not,
  LessThan,
  LessThanOrEqual,
  MoreThan,
  MoreThanOrEqual,
  ILike,
  In,
  FindOperator,
} from 'typeorm';

/* 接口 */
// 分页
export interface Pagination {
  page: number;
  limit: number;
  pagesize: number;
  offset: number;
}
// 排序
export interface Sorting {
  property: string;
  direction: string;
}
// 过滤
export interface Filtering {
  property: string;
  rule: string;
  value: string;
}
// 查询
export interface Search extends Pagination {
  order?: {
    [x: string]: string;
  };
  where?:
    | {
        [x: string]: FindOperator<any>;
      }
    | {
        [x: string]: string;
      };
}

// 过滤规则枚举
export enum FilterRule {
  EQUALS = 'eq',
  NOT_EQUALS = 'neq',
  GREATER_THAN = 'gt',
  GREATER_THAN_OR_EQUALS = 'gte',
  LESS_THAN = 'lt',
  LESS_THAN_OR_EQUALS = 'lte',
  LIKE = 'like',
  NOT_LIKE = 'nlike',
  IN = 'in',
  NOT_IN = 'nin',
  IS_NULL = 'isnull',
  IS_NOT_NULL = 'isnotnull',
}

/* 入参及返回定义 */
export interface SearchDto {
  page: number;
  pagesize: number;
}

export interface SearchReturn {
  total: number;
  rows: any[];
  page: number;
  pagesize: number;
}

// 分页修饰器
const paginationParamsFun = (data, ctx: ExecutionContext): Pagination => {
  const req: Request = ctx.switchToHttp().getRequest();
  const page = parseInt(req.query.page as string);
  const pagesize = parseInt(req.query.pagesize as string);

  // 检查page和pagesize是否正确
  if (isNaN(page) || page <= 0 || isNaN(pagesize) || pagesize <= 0) {
    throw new BadRequestException('分页参数错误');
  }
  // 限制分页条数
  if (pagesize > 100) {
    throw new BadRequestException('分页不可超过100条');
  }

  // 计算分页器参数
  const limit = pagesize;
  const offset = (page - 1) * limit;
  return { page, limit, pagesize, offset };
};
export const PaginationParams = createParamDecorator(paginationParamsFun);

// 排序修饰器
const sortingParamsFun = (data, ctx: ExecutionContext): Sorting => {
  const req: Request = ctx.switchToHttp().getRequest();
  const sort = req.query.sort as string;
  if (!sort) return null;

  // 检查参数是否为数组
  if (Array.isArray(data)) throw new BadRequestException('排序参数必须为数组');

  // 检查排序参数的格式
  const sortPattern = /^([a-zA-Z0-9]+):(asc|desc)$/;
  if (!sort.match(sortPattern))
    throw new BadRequestException('排序参数格式错误');

  // 检查属性名称和排序方向是否有效
  const [property, direction] = sort.split(':');
  if (!data.includes(property))
    throw new BadRequestException(`排序属性错误: ${property}`);

  return { property, direction };
};
export const SortingParams = createParamDecorator(sortingParamsFun);

// 过滤修饰器
const filteringParamsFun = (data, ctx: ExecutionContext): Filtering => {
  const req: Request = ctx.switchToHttp().getRequest();
  const filter = req.query.filter as string;
  if (!filter) return null;

  // 检查参数是否为数组
  if (Array.isArray(data)) throw new BadRequestException('过滤参数必须为数组');

  // 验证过滤器的格式，如果规则是“isNull”或“isNotNull”，则不需要有值
  if (
    !filter.match(
      /^[a-zA-Z0-9_]+:(eq|neq|gt|gte|lt|lte|like|nlike|in|nin):[a-zA-Z0-9_,]+$/,
    ) &&
    !filter.match(/^[a-zA-Z0-9_]+:(isnull|isnotnull)$/)
  ) {
    throw new BadRequestException('过滤参数错误');
  }

  // 检查规则和属性是否有效
  const [property, rule, value] = filter.split(':');
  if (!data.includes(property))
    throw new BadRequestException(`过滤属性错误: ${property}`);
  if (!Object.values(FilterRule).includes(rule as FilterRule))
    throw new BadRequestException(`过滤规则错误: ${rule}`);

  return { property, rule, value };
};
export const FilteringParams = createParamDecorator(filteringParamsFun);

// 查询修饰器
export const SearchParams = createParamDecorator(
  (data, ctx: ExecutionContext): Search => {
    const where = getWhere(filteringParamsFun(data, ctx));
    const order = getOrder(sortingParamsFun(data, ctx));
    const { page, limit, pagesize, offset } = paginationParamsFun(data, ctx);
    return { page, limit, pagesize, offset, order, where };
  },
);

export const getOrder = (sort: Sorting) =>
  sort ? { [sort.property]: sort.direction } : {};

export const getWhere = (filter: Filtering) => {
  if (!filter) return {};

  if (filter.rule == FilterRule.IS_NULL) return { [filter.property]: IsNull() };
  if (filter.rule == FilterRule.IS_NOT_NULL)
    return { [filter.property]: Not(IsNull()) };
  if (filter.rule == FilterRule.EQUALS)
    return { [filter.property]: filter.value };
  if (filter.rule == FilterRule.NOT_EQUALS)
    return { [filter.property]: Not(filter.value) };
  if (filter.rule == FilterRule.GREATER_THAN)
    return { [filter.property]: MoreThan(filter.value) };
  if (filter.rule == FilterRule.GREATER_THAN_OR_EQUALS)
    return { [filter.property]: MoreThanOrEqual(filter.value) };
  if (filter.rule == FilterRule.LESS_THAN)
    return { [filter.property]: LessThan(filter.value) };
  if (filter.rule == FilterRule.LESS_THAN_OR_EQUALS)
    return { [filter.property]: LessThanOrEqual(filter.value) };
  if (filter.rule == FilterRule.LIKE)
    return { [filter.property]: ILike(`%${filter.value}%`) };
  if (filter.rule == FilterRule.NOT_LIKE)
    return { [filter.property]: Not(ILike(`%${filter.value}%`)) };
  if (filter.rule == FilterRule.IN)
    return { [filter.property]: In(filter.value.split(',')) };
  if (filter.rule == FilterRule.NOT_IN)
    return { [filter.property]: Not(In(filter.value.split(','))) };
};
